#include "vcd-download.h"
#include "vcd-log.h"
#include "vcd-common.h"
#include "./vcd-conf/vcd-conf.h"

typedef struct _DownloadContext{
    GThread *download_thread;
    GMutex* thd_mutex;
    GCond*  thd_cond;
    GSList* net_req_list;
    gboolean open_net_debug_info;
}DownloadContext;

static volatile gboolean download_flag = TRUE;
static DownloadContext* g_dc = NULL;

static int create_download_context(void);
static void free_download_context(void);
static gboolean curl_get_json_in_thread(void* ptr);
static size_t curl_get_virt_info(void *ptr, size_t size, size_t nmemb, void *userp);
static void curl_common_callback(gpointer callback, gpointer data);

static struct curl_slist* curl_common_base_header(struct curl_slist *list){


    //list = curl_slist_append(list, g_final_ticket);
    //list = curl_slist_append(list, g_token);

    // set http header Authorization
    if(g_vcd_conf && g_vcd_conf->local_model_check_status){

        static char* api_token;
        //if(!api_token)
            api_token = vcd_common_get_apitoken();
        if(api_token){
            char str[500] = {0};
            sprintf(str,"Authorization: PVEAPIToken=%s=%s",PVE_API_TOKEN_KEY,api_token);
            list = curl_slist_append(list, str);
        }
    }
    return list;
}

static void curl_common_callback(gpointer callback, gpointer data)
{
    g_return_if_fail(callback != NULL);
    gdk_threads_add_idle((GSourceFunc)callback, data);
}

void network_response_free(struct network_response** pp_res)
{
    if (pp_res == NULL || *pp_res == NULL)
        return;
    struct network_response* res = *pp_res;
    SAFE_FREE(res->memory);
    SAFE_FREE(res->error_msg);
    SAFE_FREE(res);
    *pp_res = NULL;
}

static int create_download_context(void)
{
    GMutex* thd_mutex = NULL;
    GCond*  thd_cond = NULL;
    DownloadContext* dc = (DownloadContext*)g_malloc(sizeof(DownloadContext));
    g_return_val_if_fail(dc != NULL, -1);
    thd_mutex = g_malloc0(sizeof(GMutex));
    if (thd_mutex == NULL) {
        LOG_ERROR("new mutex failed");
        goto fail;
    }

    g_mutex_init(thd_mutex);

    thd_cond = g_malloc0(sizeof(GCond));
    if(thd_cond == NULL){
        LOG_ERROR("new cond failed");
        goto fail;
    }

    g_cond_init(thd_cond);
    dc->thd_cond = thd_cond;
    dc->thd_mutex = thd_mutex;
    dc->net_req_list = NULL;
    dc->open_net_debug_info = FALSE;
    g_dc = dc;
    return 0;
fail:
    if (thd_mutex) {
        g_mutex_clear(thd_mutex);
        thd_mutex = NULL;
    }
    if(thd_cond) {
        g_cond_clear(thd_cond);
        thd_cond = NULL;
    }
    SAFE_FREE(dc);
    return -1;
}

static void free_download_context(void)
{
    if (g_dc == NULL)
        return;

    if (g_dc->download_thread) {
        g_thread_unref(g_dc->download_thread);
        g_dc->download_thread = NULL;
    }

    if (g_dc->thd_mutex) {
        g_mutex_clear(g_dc->thd_mutex);
        g_dc->thd_mutex = NULL;
    }

    if (g_dc->thd_cond) {
        g_cond_clear(g_dc->thd_cond);
        g_dc->thd_cond = NULL;
    }
    g_free(g_dc);
    g_dc = NULL;
}

int start_download_thread(gboolean open_debug_info)
{
    int res = create_download_context();
    g_return_val_if_fail(res == 0, -1);
    GThread * thd = NULL;
    thd = g_thread_new("vcd_download", (GThreadFunc)curl_get_json_in_thread, NULL);
    if (thd == NULL) {
        free_download_context();
        LOG_ERROR("create new thread error");
        return -1;
    }

    g_dc->download_thread = thd;
    g_dc->open_net_debug_info = open_debug_info;
    if (g_dc->open_net_debug_info)
        M_DEBUG("start download thread success, net debug opened");
    else
        M_DEBUG("start download thread success, net debug closed");
    return 0;
}

void stop_download_thread(void)
{
    M_DEBUG("stop download thread");
    download_flag = FALSE;
    g_return_if_fail(g_dc != NULL);

    g_mutex_lock(g_dc->thd_mutex);

    //free left memory
    //while (g_slist_length(g_dc->net_req_list) > 0) {
    //    gpointer first = g_slist_nth_data(g_dc->net_req_list,0);
    //    g_dc->net_req_list = g_slist_remove(g_dc->net_req_list,first);
    //    SAFE_FREE(first);
    //}

    //signal wait thread
    g_cond_signal(g_dc->thd_cond);
    g_mutex_unlock(g_dc->thd_mutex);

    //wait thread exit
    g_thread_join(g_dc->download_thread);
	g_dc->download_thread = NULL;

    free_download_context();
}


void net_req_list_add(struct network_request_arg* pt)
{	
    g_return_if_fail(pt != NULL);
    g_mutex_lock(g_dc->thd_mutex);
    g_dc->net_req_list = g_slist_append(g_dc->net_req_list, (gpointer)pt);
    g_cond_signal(g_dc->thd_cond);
    g_mutex_unlock(g_dc->thd_mutex);
}

static size_t curl_get_virt_info(void *ptr, size_t size, size_t nmemb, void *userp)//存入文件的函数
{
    size_t real_size = size * nmemb;
    struct memory_struct *mem = (struct memory_struct *)userp;

    if (mem->memory == NULL)
        mem->memory = (char *)malloc(real_size + 1);
    else
        mem->memory = (char *)realloc(mem->memory, mem->size + real_size + 1);

    if (mem->memory == NULL)
        return 0;

    memcpy(mem->memory + mem->size, ptr, real_size);
    mem->size += real_size;
    mem->memory[mem->size] = 0;
    return real_size;
}

static void deal_one_network_req(struct network_request_arg *tmp)
{
    CURL *curl = NULL;
    CURLcode res;
    struct network_response *net_res = NULL;
    struct memory_struct chunk;
    chunk.size = 0;
    chunk.memory = NULL;
    long response_code;//返回状态码

    curl = curl_easy_init();

    //"https://192.168.112.100:8006/api2/json/access/ticket");
    curl_easy_setopt(curl, CURLOPT_URL, tmp->url);

    if (tmp->http_method == CURLOPT_POST) {
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, tmp->post_fields);//-d
    }

    struct curl_slist *list = curl_common_base_header(tmp->list);
    if (NULL != list) {
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
    }

    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);//-k
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);//-k
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, tmp->timeout);
    //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);//显示所有日志信息

    //curl_easy_setopt(curl, CURLOPT_HEADER, 1L);//显示头信息

    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_get_virt_info);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);

    res = curl_easy_perform(curl);
        
    if (tmp->callback == NULL){
        SAFE_FREE(chunk.memory);
        curl_easy_cleanup(curl);
        curl = NULL;
        return;
    }

    net_res = (struct network_response*)calloc(1,sizeof(struct network_response));
    if (net_res == NULL) {
        curl_common_callback(tmp->callback, (gpointer)NULL);
        SAFE_FREE(chunk.memory);
        curl_easy_cleanup(curl);
        curl = NULL;
        return;
    }

    if (res != CURLE_OK) {
        net_res->error_msg = strdup(curl_easy_strerror(res));
    } else {
        net_res->error_msg = NULL;
        curl_easy_getinfo(curl,  CURLINFO_RESPONSE_CODE, &response_code);
        if (response_code == 401) {
            M_DEBUG("We received response_code == 401\n");
            net_res->error_msg = strdup("用户名密码验证失败(401)");
        }
        net_res->response_code = response_code;
    }

    curl_easy_cleanup(curl);
    curl = NULL;

    net_res->data_priv = tmp->data_priv;
    net_res->memory = chunk.memory;
    if (g_dc->open_net_debug_info) {
        M_DEBUG("\n\nrequest url:%s\npost_field:%s\nreturn json:%s\nerror_msg:%s",
            tmp->url, tmp->post_fields ,
            chunk.memory ?chunk.memory :"NULL",
            net_res->error_msg ?net_res->error_msg:"NULL");
    }

    curl_common_callback(tmp->callback, net_res);

    if (tmp->list)
        curl_slist_free_all(tmp->list);
}

static gboolean curl_get_json_in_thread(void* ptr)
{
    while (download_flag)
    {
        g_mutex_lock(g_dc->thd_mutex);
        if (g_slist_length(g_dc->net_req_list) <= 0) {
            g_cond_wait(g_dc->thd_cond, g_dc->thd_mutex);
        }
        gpointer first = g_slist_nth_data(g_dc->net_req_list, 0);
        g_dc->net_req_list = g_slist_remove(g_dc->net_req_list, first);
        g_mutex_unlock(g_dc->thd_mutex);

        if (first) {
            deal_one_network_req(first);
            free(first);
            first = NULL;
        }
    }
    return FALSE;
}


network_response* vcd_download_do_request_sync(struct network_request_arg *tmp)
{
    CURL *curl = NULL;
    CURLcode res;
    struct network_response *net_res = NULL;
    struct memory_struct chunk;
    chunk.size = 0;
    chunk.memory = NULL;
    long response_code;//返回状态码

    curl = curl_easy_init();

    //"https://192.168.112.100:8006/api2/json/access/ticket");
    curl_easy_setopt(curl, CURLOPT_URL, tmp->url);

    if (tmp->http_method == CURLOPT_POST) {
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, tmp->post_fields);//-d
    }

    if (NULL != tmp->list) {
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, tmp->list);
    }

    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);//-k
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);//-k
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, tmp->timeout);
    //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);//显示所有日志信息

    //curl_easy_setopt(curl, CURLOPT_HEADER, 1L);//显示头信息

    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_get_virt_info);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);

    res = curl_easy_perform(curl);

    net_res = (struct network_response*)calloc(1,sizeof(struct network_response));
    if (net_res == NULL) {
        SAFE_FREE(chunk.memory);
        curl_easy_cleanup(curl);
        curl = NULL;
        return 0;
    }

    if (res != CURLE_OK) {
        net_res->error_msg = strdup(curl_easy_strerror(res));
    } else {
        net_res->error_msg = NULL;
        curl_easy_getinfo(curl,  CURLINFO_RESPONSE_CODE, &response_code);
        if (response_code == 401) {
            M_DEBUG("We received response_code == 401\n");
            net_res->error_msg = strdup("用户名密码验证失败(401)");
        }
        net_res->response_code = response_code;
    }

    curl_easy_cleanup(curl);
    curl = NULL;

    net_res->data_priv = tmp->data_priv;
    net_res->memory = chunk.memory;

    if (tmp->list)
        curl_slist_free_all(tmp->list);

    return net_res;
}
